package org.zmeu.blog.simple.generics;

import static org.testng.Assert.assertEquals;

import java.io.Serializable;

import org.testng.annotations.Test;

/**
 * Unit tests for {@link Generics}
 * 
 * @author Andrei Zagorneanu
 * @since 2011-07-31
 * @see <a href="http://azagorneanu.blogspot.com">http://azagorneanu.blogspot.com</a>
 */
public class GenericsTest {
	public static class TestGenericSuperclass<T, V> {
		// test class, empty
	}

	public static class TestGenericSubclass extends TestGenericSuperclass<Integer, String> {
		// test class, empty
	}

	public static class TestSubclass extends TestGenericSubclass {
		// test class, empty
	}

	public static interface GenericSuperinterface<T, V> {
		// test class, empty
	}

	public static interface GenericSubinterface extends GenericSuperinterface<Integer, String> {
		// test class, empty
	}

	public static interface Subinterface extends Serializable, Cloneable, GenericSubinterface {
		// test class, empty
	}

	public static class TestClassWithInterface implements GenericSuperinterface<Integer, String> {
		// test class, empty
	}

	public static class TestSubclassWithInterface extends TestClassWithInterface {
		// test class, empty
	}

	public static class TestGenericClassWithBounds<T extends Number> {
		// test class, empty
	}

	@Test
	public void extractTypeArgumentFromClassFromGenericClass() {
		assertEquals(Generics.extractTypeArgumentFromClass(TestGenericSubclass.class),
				Integer.class);
		assertEquals(Generics.extractTypeArgumentFromClass(TestGenericSubclass.class, 0),
				Integer.class);
		assertEquals(Generics.extractTypeArgumentFromClass(TestGenericSubclass.class, 1),
				String.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassInvalidArgNumberPos() {
		Generics.extractTypeArgumentFromClass(TestGenericSubclass.class, 2);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassInvalidArgNumberNeg() {
		Generics.extractTypeArgumentFromClass(TestGenericSubclass.class, -1);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassFromSuperclass() {
		Generics.extractTypeArgumentFromClass(TestGenericSuperclass.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassFromArray() {
		Generics.extractTypeArgumentFromClass(int[].class);
	}

	@Test
	public void extractTypeArgumentFromClassFromClassWithGenericSuperclass() {
		assertEquals(Generics.extractTypeArgumentFromClass(TestSubclass.class), Integer.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassFromClassWithInterfaceFailExtractFromClass() {
		assertEquals(Generics.extractTypeArgumentFromClass(TestClassWithInterface.class),
				String.class);
	}

	@Test
	public void extractTypeArgumentFromInterfaceFromClassWithInterface() {
		assertEquals(Generics.extractTypeArgumentFromInterface(TestClassWithInterface.class),
				Integer.class);
		assertEquals(Generics.extractTypeArgumentFromInterface(TestClassWithInterface.class, 0),
				Integer.class);
		assertEquals(Generics.extractTypeArgumentFromInterface(TestClassWithInterface.class, 1),
				String.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromClassFromSubClassWithInterfaceFailExtractFromClass() {
		assertEquals(Generics.extractTypeArgumentFromClass(TestSubclassWithInterface.class),
				Integer.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void extractTypeArgumentFromInterfaceFromSubClassWithInterfaceFailExtractFromInterface() {
		assertEquals(Generics.extractTypeArgumentFromInterface(TestSubclassWithInterface.class),
				Integer.class);
	}

	@Test
	public void extractTypeArgumentFromInterfaceFromSubClassWithInterfaceUsingSuperclass() {
		assertEquals(Generics.extractTypeArgumentFromInterface(TestSubclassWithInterface.class
				.getSuperclass()), Integer.class);
	}

	@Test
	public void extractTypeArgumentFromInterfaceFromGenericSubInterface() {
		assertEquals(Generics.extractTypeArgumentFromInterface(GenericSubinterface.class),
				Integer.class);
	}

	@Test
	public void extractTypeArgumentFromInterfaceFromSubInterfaceWichExtendsMultipleInterfaces() {
		assertEquals(Generics.extractTypeArgumentFromInterface(Subinterface.class), Integer.class);
	}

	@Test
	public void testExtractParameterBounds() {
		assertEquals(Generics.extractParameterBounds(TestGenericClassWithBounds.class)[0],
				Number.class);
		assertEquals(Generics.extractParameterBounds(TestGenericClassWithBounds.class, 0)[0],
				Number.class);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void testExtractParameterBoundsInvalidArgPos() {
		Generics.extractParameterBounds(TestGenericClassWithBounds.class, 1);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void testExtractParameterBoundsInvalidArgNeg() {
		Generics.extractParameterBounds(TestGenericClassWithBounds.class, -1);
	}

	@Test(expectedExceptions = IllegalArgumentException.class)
	public void testExtractParameterBoundsNoParams() {
		Generics.extractParameterBounds(TestGenericSubclass.class);
	}

	@Test
	public void testExtractParameterBoundsNoBounds() {
		assertEquals(Generics.extractParameterBounds(TestGenericSuperclass.class)[0], Object.class);
	}

}
